package org.example.netty.nio.selector;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class selectorStu {
    public static void main(String[] args) throws Exception {
        int[] ports = new int[5];
        ports[0] = 5000;
        ports[1] = 5001;
        ports[2] = 5002;
        ports[3] = 5003;
        ports[4] = 5004;
        // 创建选择器, 类似于多路复用器, 类似于reactor模式中的selector, 类似于netty中的selector
        Selector selector = Selector.open();
        for (int i = 0; i < ports.length; ++i) {
            // 创建ServerSocketChannel, 并设置为非阻塞模式
            // 类似于netty中的bossGroup, 负责接收客户端的连接请求
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 设置为非阻塞模式, 这样就不会因为accept()方法阻塞而导致整个线程被挂起
            serverSocketChannel.configureBlocking(false);
            // 绑定端口号, 并启动接收
            ServerSocket serverSocket = serverSocketChannel.socket();
            InetSocketAddress address = new InetSocketAddress(ports[i]);
            serverSocket.bind(address);
            // 将该通道注册到选择器上, 并指定"监听接收事件", 类似于netty中的bossGroup的handler
            // 类似于netty中的bossGroup的handler, 负责接收客户端连接请求, 并把客户端连接的socketChannel注册到selector上
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("监听端口：" + ports[i]);
        }
        while (true) {
            // 阻塞等待, 直到至少有一个通道在你注册的事件上就绪了。
            int numbers = selector.select();
            System.out.println("numbers:" + numbers);
            // 返回那些已经被服务选择器选中的键的集合(Set)
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            System.out.println("selectedKeys:" + selectionKeys);
            // 迭代遍历这些键
            Iterator<SelectionKey> iter = selectionKeys.iterator();
            while (iter.hasNext()) {
                // 获取到每一个键
                SelectionKey selectionKey = iter.next();
                // 判断该键对应的通道是否已经准备好被接收
                // 类似于netty中的workerGroup的handler, 负责处理客户端连接请求
                if (selectionKey.isAcceptable()) {
                    // 如果是, 则获取该通道,
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                    // 接受该连接
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    // 设置为非阻塞模式, 这样就不会因为read()方法阻塞而导致整个线程被挂起
                    socketChannel.configureBlocking(false);
                    // 将该通道注册到选择器上, 并指定"监听读事件", 类似于netty中的workerGroup的handler
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    // 从集合中移除当前的键, 因为我们已经处理了它, 否则它将永远不会被再次检索。
                    iter.remove();

                    System.out.println("获得客户端连接：" + socketChannel);
                } else if (selectionKey.isReadable()) {
                    // 如果是, 则获取该通道
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    int bytesRead = 0;
                    while (true) {
                        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
                        byteBuffer.clear();
                        // 读取数据到缓冲区
                        int read = socketChannel.read(byteBuffer);

                        if (read <= 0) {
                            break;
                        }
                        byteBuffer.flip();
                        // 将缓冲区的数据写回客户端
                        socketChannel.write(byteBuffer);
                        bytesRead += read;
                    }
                    System.out.println("读取：" + bytesRead + ",来自于：" + socketChannel);
                    iter.remove();
                }
            }
        }

    }
}